import { createVNode, Directive, DirectiveBinding, render } from "vue"
import ContextmenuComponent from "@/components/Contextmenu/index.vue"

const CTX_CONTEXTMENU_HANDLER = 'CTX_CONTEXTMENU_HANDLER'

const contextmenuList = (el: HTMLElement, event: MouseEvent, binding: DirectiveBinding) => {
  event.stopPropagation()
  event.preventDefault()

  // binding.value：就是传递给指令的值
  const menus = binding.value(el)

  if (!menus) return

  // HTMLDivElement 接口提供了一些特殊属性（它也继承了通常的 HTMLElement 接口）来操作 <div> 元素
  let container: HTMLDivElement | null = null

  // 移除右键菜单并取消相关的事件监听
  const removeContextmenu = () => {
    if (container) {
      document.body.removeChild(container)
      container = null
    }
    el.classList.remove('contextmenu-active')
    document.body.removeEventListener('scroll', removeContextmenu)
    window.removeEventListener('resize', removeContextmenu)
  }

  // 创建自定义菜单
  const options = {
    axis: { x: event.x, y: event.y },
    el,
    menus,
    removeContextmenu
  }
  // 创建一个名为div的HTML元素
  container = document.createElement('div')
  /**
   * 渲染函数：一般只会在需要处理高度动态渲染逻辑的可重用组件中使用
   * 此处需要用渲染函数的原因是，因为鼠标右键出现的选项卡是一个组件且该组件的位置随着鼠标的位置而动态渲染ContextmenuComponent
   * 参数1：虚拟节点 | 类式组件 | 组件
   * 参数2：元素属性
   * 参数3：元素子节点，支持createVNode嵌套
   */
  const vm = createVNode(ContextmenuComponent, options)
  /**
   * 参数1：要被渲染的虚拟DOM，必选
   * 参数2：目标容器
   */
  render(vm, container)
  document.body.appendChild(container)

  // 为目标节点添加菜单激活状态的className
  el.classList.add('contextmenu-active')

  // 当页面发生变化时，移除右键出现的菜单
  document.body.addEventListener('scroll', removeContextmenu)
  // 当window调整大小时会触发 resize 事件，移除右键出现的菜单
  window.addEventListener('resize', removeContextmenu)
}

const ContextmenuDirective: Directive = {
  /**
   * @param el 指令绑定到的元素。这可以用于直接操作 DOM。
   * @param binding 一个对象
   */
  mounted(el: HTMLElement, binding) {
    el[CTX_CONTEXTMENU_HANDLER] = (event: MouseEvent) => contextmenuList(el, event, binding)
    // 此处的contextmenu是原生DOM的鼠标右键事件
    el.addEventListener('contextmenu', el[CTX_CONTEXTMENU_HANDLER])
  },
  unmounted(el: HTMLElement) {
    if (el && el[CTX_CONTEXTMENU_HANDLER]) {
      el.removeEventListener('contextmenu', el[CTX_CONTEXTMENU_HANDLER])
      delete el[CTX_CONTEXTMENU_HANDLER]
    }
  }
}

export default ContextmenuDirective